<!DOCTYPE html>
<html>
<head>
  <title>Grouping</title>
  <meta name="description" content="A diagram holding groups that incrementally grow the diagram as groups are expanded." />
  <!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
  <meta charset="UTF-8">
  <script src="go.js"></script>
  <link href="../assets/css/goSamples.css" rel="stylesheet" type="text/css" />  <!-- you don't need to use this -->
  <script src="goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
  <script id="code">
    function init() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // Diagram refers to its DIV HTML element by id
          {
            // start everything in the middle of the viewport
            initialContentAlignment: go.Spot.Center,
            layout: $(go.TreeLayout,  // the layout for the entire diagram
                      {
                        angle: 90,
                        arrangement: go.TreeLayout.ArrangementHorizontal,
                        isRealtime: false
                      })
          });

      // define the node template for non-groups
      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          $(go.Shape, "Rectangle",
            { stroke: null, strokeWidth: 0 },
            new go.Binding("fill", "key")),
          $(go.TextBlock,
            { margin: 7, font: "Bold 14px Sans-Serif" },
            //the text, color, and key are all bound to the same property in the node data
            new go.Binding("text", "key"))
        );

      myDiagram.linkTemplate =
        $(go.Link,
          { routing: go.Link.Orthogonal, corner: 10 },
          $(go.Shape, { strokeWidth: 2 }),
          $(go.Shape, { toArrow: "OpenTriangle" })
        );

      // define the group template
      myDiagram.groupTemplate =
        $(go.Group, "Auto",
          { // define the group's internal layout
            layout: $(go.TreeLayout,
                      { angle: 90, arrangement: go.TreeLayout.ArrangementHorizontal, isRealtime: false }),
            // the group begins unexpanded;
            // upon expansion, a Diagram Listener will generate contents for the group
            isSubGraphExpanded: false,
            // when a group is expanded, if it contains no parts, generate a subGraph inside of it
            subGraphExpandedChanged: function(group) {
              if (group.memberParts.count === 0) {
                randomGroup(group.data.key);
              }
            }
          },
          $(go.Shape, "Rectangle",
            { fill: null, stroke: "gray", strokeWidth: 2 }),
          $(go.Panel, "Vertical",
            { defaultAlignment: go.Spot.Left, margin: 4 },
            $(go.Panel, "Horizontal",
              { defaultAlignment: go.Spot.Top },
              // the SubGraphExpanderButton is a panel that functions as a button to expand or collapse the subGraph
              $("SubGraphExpanderButton"),
              $(go.TextBlock,
                { font: "Bold 18px Sans-Serif", margin: 4 },
                new go.Binding("text", "key"))
            ),
            // create a placeholder to represent the area where the contents of the group are
            $(go.Placeholder,
              { padding: new go.Margin(0, 10) })
          )  // end Vertical Panel
        );  // end Group

      // generate the initial model
      randomGroup();
    }

    // Generate a random number of nodes, including groups.
    // If a group's key is given as a parameter, put these nodes inside it
    function randomGroup(group) {
      // all modification to the diagram is within this transaction
      myDiagram.startTransaction("addGroupContents");
      var addedKeys = [];  // this will contain the keys of all nodes created
      var groupCount = 0;  // the number of groups in the diagram, to determine the numbers in the keys of new groups
      myDiagram.nodes.each(function(node) {
        if (node instanceof go.Group) groupCount++;
      });
      // create a random number of groups
      // ensure there are at least 10 groups in the diagram
      var groups = Math.floor(Math.random() * 2);
      if (groupCount < 10) groups += 1;
      for (var i = 0; i < groups; i++) {
        var name = "group" + (i + groupCount);
        myDiagram.model.addNodeData({ key: name, isGroup: true, group: group });
        addedKeys.push(name);
      }
      var nodes = Math.floor(Math.random() * 4) + 2;
      // create a random number of non-group nodes
      for (var i = 0; i < nodes; i++) {
        var color = go.Brush.randomColor();
        // make sure the color, which will be the node's key, is unique in the diagram before adding the new node
        if (myDiagram.findPartForKey(color) === null) {
          myDiagram.model.addNodeData({ key: color, group: group });
          addedKeys.push(color);
        }
      }
      // add at least one link from each node to another
      // this could result in clusters of nodes unreachable from each other, but no lone nodes
      var arr = [];
      for (var x in addedKeys) arr.push(addedKeys[x]);
      arr.sort(function (x, y) { return Math.random(2)-1; });
      for (var i = 0; i < arr.length; i++) {
        var from = Math.floor(Math.random() * (arr.length - i)) + i;
        if (from !== i) {
          myDiagram.model.addLinkData({ from: arr[from], to: arr[i] });
        }
      }
      myDiagram.commitTransaction("addGroupContents");
    }
  </script>
</head>
<body onload="init()">
<div id="sample">
  <h3>GoJS Grouping</h3>
  <div id="myDiagramDiv" style="height:600px;width:100%;border:1px solid black"></div>
  <p>
  This sample demonstrates subgraphs that are created only as groups are expanded.
  </p>
  <p>
  The model is initially a random number of nodes, including some groups, in a tree layout.
  When a group is expanded, the <a>Group.subGraphExpandedChanged</a> event handler calls a function to generate a random number of nodes
  in a tree layout inside the group if it did not contain none any.
  Each non-group node added has a unique random color, and links are added by giving each node one link to another node.
  </p>
  <p>
  The addition of nodes and links is performed within a transaction to ensure that the diagram updates itself properly.
  The diagram's tree layout and the tree layouts within each group are performed again when a sub-graph is expanded or collapsed.
  </p>
</div>
</body>
</html>